<?php

namespace CommonBundle\Serializer\Normalizer;

use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;

class FlatNormalizer extends GetSetMethodNormalizer
{
    protected function extractAttributes($object, $format = null, array $context = [])
    {
        $attributes = parent::extractAttributes($object, $format, $context);

        // add to string to data
        $attributes[] = '__toString';

        return $attributes;
    }

    protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
    {
        $ucfirsted = ucfirst($attribute);

        $getter = 'get'.$ucfirsted;
        if (\is_callable(array($object, $getter))) {

            $object = $object->$getter();

            $reduceTransform = function($object) {
                $res = [];
                $res['id'] = $object->getId();
                if (method_exists($object, '__toString'))
                    $res['__toString'] = $object->__toString();
                if (method_exists($object, '__metadata'))
                    $res['__metadata'] = $object->__metadata();
                if (property_exists($object, '__metadata'))
                    $res['__metadata'] = $object->__metadata;

                return $res;
            };

            // string, including json object, exclude numeric string
            if (is_string($object) && !is_numeric($object)) {
                $json = json_decode($object, true);
                return json_last_error() === JSON_ERROR_NONE ? $json: $object;
            }

            // when object is a relations
            elseif (method_exists($object, 'getId')) {
                return $reduceTransform($object);
            }

            // when object is array collections
            elseif ($object instanceof \Traversable) {
                $tmp_object = [];
                foreach ($object as $o) {
                    if (method_exists($o, 'getId')) {
                        $tmp_object[] = $reduceTransform($o);
                    }
                }
                return $tmp_object;
            }

            // normal objects
            else {
                return $object;
            }
        }

        $isser = 'is'.$ucfirsted;
        if (\is_callable(array($object, $isser))) {
            return $object->$isser();
        }

        $haser = 'has'.$ucfirsted;
        if (\is_callable(array($object, $haser))) {
            return $object->$haser();
        }

        // __toString
        if (\is_callable(array($object, '__toString'))) {
            return $object->__toString();
        }
    }
}
